/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import java.util.ArrayList;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * The Panel in which bar graph is drawn
 * @author Kay van Bree
 */
public class BarGraphPanel extends JPanel implements ManagementPanel {
	private ArrayList<String[]> info;
	private String infoName;
	private double maxValue;
	private int size;
	private int barWidth;
	private double left;
	private double right;
	private double width;
	private double height;
	private double bottom;
	private double top;
	private int barSpacing;
	private double barPart;
	private double graphHeight;
	private double graphWidth;
	private int cap;
	
	public BarGraphPanel(){
		super();
		
		width = 800;
		height = 480;
		setSize((int) width, (int) height);
		
		// The maximum number of results to show
		cap = 50;
		
		this.infoName = "Management information";
		this.add(new JLabel("No query selected yet. Please select a query from the menu to your left."));
	}

	/**
	 * Sets both x and y, min and max and the info list
	 * @author Kay van Bree
	 * @param info 
	 */
	@Override
	public void setManagementInfo(ArrayList<String[]> info) {
		reset();
		this.info = info;
		
		// Set important values
		this.maxValue = getMaxValue(info);
		this.size = info.size();

		setSizes();
		validate();
		repaint();
	}
	
	/**
	 * Resets the panel, taken from TablePanel
	 * @author Joepe Kemperman
	 */
	public void reset() {
		invalidate();
		removeAll();
	}
	
	/**
	 * Set the sizes needed for calculating bar heights etc.
	 * @author Kay van Bree
	 */
	protected void setSizes(){		
		left = (width / 100) * 11;
		right = width - left*2;
		top = (height / 100) * 11;
		bottom = height - top*2;
		
		// The dimensions of the graph
		graphHeight = bottom - top;
		graphWidth = right - left;
		
		// barWidth is... Just guess what it is.
		barWidth = (int) ((double) (graphWidth / size) * 0.95);
		
		// barSpacing is the space between the bars
		barSpacing = (int) (graphWidth - (barWidth*size)) / size;
		if(barSpacing == 0){ 
			barSpacing++;
			barWidth--;
		}
		
		// barPart is the height of a bar of value 1
		barPart = graphHeight / maxValue;
	}
	
	/**
	 * Return the max value of the info arraylist
	 * @author Kay van Bree
	 * @param info 
	 */
	private int getMaxValue(ArrayList<String[]> info){
		int max = 0;
		for(String[] row: info){	
			if(Integer.valueOf(row[1]) > max){
				max = Integer.valueOf(row[1]);
			}
		}
		return max;
	}
	
	/**
	 * Draw the damn thing!
	 * @author Kay van Bree
	 * @param g 
	 */
	@Override
	protected void paintComponent(Graphics g){
		super.paintComponent(g);
		if(isGraph(info)){
			g.setColor(Color.BLACK);
			drawTitle(g);
			drawBlocks(g);
			drawAxes(g);
			if(size <= cap){
				drawBars(g);
			} else {
				drawError(g);
			}
		}
	}
	
	/**
	 * Set an error displaying to many results
	 * @author Kay van Bree
	 * @param g 
	 */
	private void drawError(Graphics g){
		g.setColor(Color.BLACK);
		g.drawString("Too many results to show!", (int)(right-left*2)/2, (int)(bottom-top*2)/2);
	}
	
	/**
	 * Draw the names under the bars
	 * @author Kay van Bree
	 * @param g 
	 */
	private void drawName(Graphics g, String name, int x){
		Graphics2D g2d = (Graphics2D) g.create();
		
		// Rotate
		g2d.rotate(Math.PI/2.0, x + (barWidth/4), bottom + 3);
		
		// Print
		g2d.setColor(Color.BLACK);
		g2d.drawString(name, (int) (x + (barWidth/4)), (int) bottom  + 3);
		
		// Reset transformation
		g2d.dispose();
	}
	
	/**
	 * Draw the lines behind the bars
	 * @author Kay van Bree
	 * @param g 
	 */
	private void drawBlocks(Graphics g){
		// Graphics for drawing the background
		Graphics2D g2d = (Graphics2D) g;
		BufferedImage image = new BufferedImage((int)(right - left), (int)(bottom - top), BufferedImage.TYPE_INT_ARGB);
		Graphics2D ig = image.createGraphics();
		
		// Paint the background
		ig.setPaint(new GradientPaint((int) (right - left), (int) (bottom), Color.LIGHT_GRAY, (int)(right - left), (int)(top), Color.white));
		ig.fillRect(0, 0, (int)(right - left), (int)(bottom - top));
		
		// Antialiasing
		ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		// Drawing actual background
		g2d.drawImage(image, (int) left, (int) top, this);
		
//		g.setColor(Color.WHITE);
//		// Draw the background
//		g.fillRect((int) left, (int) top, (int) (right - left), (int) (bottom - top));
		
		g.setColor(Color.lightGray);
		// Ten parts
		double part = maxValue / 10;
		double total = maxValue;
		
		// Draw the numbers
		for(int i=0; i<10; i++){
			double y = (((bottom-top)/10)*i)+top;
			g.drawLine(((int) left)-5, (int) y, ((int) right), (int) y);
		}
		g.drawLine((int) right, (int) top, (int) right, (int) bottom);
	}
	
	/**
	 * Draw the title of the Graph
	 * @author Kay van Bree
	 * @param g 
	 */
	private void drawTitle(Graphics g){
		int tLeft = 100;
		g.drawString(infoName, tLeft, (int) (top *0.8));
	}
	
	/**
	 * Draw the axes of this Bar Graph
	 * @author Kay van Bree
	 * @param g 
	 */
	private void drawAxes(Graphics g){
		g.setColor(Color.BLACK);
		// draw lines from (x, y) to (x, y)
		g.drawLine((int) left, (int) top, (int) left, (int) bottom);
		g.drawLine((int) left, (int) bottom, (int) right, (int) bottom);
		
		// Ten parts
		double part = maxValue / 10;
		double total = maxValue;
		
		// Draw the numbers
		for(int i=0; i<10; i++){
			double y = (((bottom-top)/10)*i)+top;
			String number = round(total);
			FontMetrics fontMetrics = g.getFontMetrics();
			g.drawString(number, (int) (left * 0.80) - fontMetrics.stringWidth(number), ((int) y)+5);
			total = (total - part);
			g.drawLine(((int) left)-5 , (int) y, ((int) left), (int) y);
		}
	}
	
	/**
	 * Rounds the number to 1 decimal
	 * @author Kay van Bree
	 * @param number
	 * @return 
	 */
	private String round(double number){
		DecimalFormat df = new DecimalFormat("#.#");
		Double dubbel = new Double(number);
		return df.format(dubbel);
	}
	
	/**
	 * Round a number, make it int
	 * @author Kay van Bree
	 * @param number
	 * @return 
	 */
	private int roundToInt(double number){
		DecimalFormat df = new DecimalFormat("#");
		Double dubbel = new Double(number);
		return Integer.valueOf(df.format(dubbel));
	}
		
	
	/**
	 * We need info right?
	 * Then draw the damn bars already!
	 * @author Kay van Bree
	 * @param g 
	 */
	private void drawBars(Graphics g){		
		double currentX = left + barSpacing + 1;
		for(String[] row: info){
			double value = Integer.valueOf(row[1]);
			double barHeight = (value * barPart);
			
			System.out.println("Value: " + value + " height: " + barHeight + " top: " + top);
			double barStart = bottom - barHeight;
			System.out.println("Barstart: " + barStart);
			barHeight = ((int) bottom - (int) barStart);
			g.setColor(Color.BLUE);
			g.fillRect((int) currentX, (int) barStart, (int) barWidth, roundToInt(barHeight));
			drawName(g, row[0], (int) currentX);
			
			currentX = (currentX + barSpacing + barWidth);
		}
	}

	/**
	 * Removes all components and shows the user the current 
	 * management information is not suitable for a pie chart.
	 */
	public void setInformationNotSuitable() {
		reset();
		
		//getGraphics().clearRect(0, 0, getWidth(), getHeight());
		add(new JLabel("This information is not suitable for Pie Chart view!"));	
		validate();
		repaint();
	}

	/**
	 * Checks if the information is suitable for Bar Graph view
	 * @author Kay van Bree
	 * @param info
	 * @return True if information can be used for a bar graph
	 */
	public boolean isGraph(ArrayList<String[]> info) {
		//throw new UnsupportedOperationException("Not yet implemented");
		if(info == null){
			return false;
		}
		if(info.size() > cap){
			return false;
		}
		String[] string = info.get(0);
		try{
			Integer.parseInt(string[1]);
		} catch(Exception e){
			return false;
		}
		return true;
	}
}
